# -*- coding:utf-8 -*-

"""
举例：
我们要通过【http】访问【www.cq315house.com】主机缺省80端口【web服务器程序】管理下的cgi程序【/315web/HtmlPage/ShowRoomsNew.aspx】
并需要查询组件{buildingid：11110017, block:'1幢（规划1号楼）'}
而我们实际的url为：http://www.cq315house.com/315web/HtmlPage/ShowRoomsNew.aspx?buildingid=11110017&block=1%u5E62%uFF08%u89C4%u52121%u53F7%u697C%uFF09
为什么要这样呢？
1.因为在http请求头报文中url为一条字符串，web服务器需要解析这条url，取出各个组件，那么rfc约定将这条url便以/,?等保留字符将各部分组件分隔开。
2.那么在我们的各个组件内部便不能出现这些保留字，这些保留字是功能符。
3.url编码便是将url各组件实体内部的非保留字符以外的字符转码成ASCII十六进制序号，并以 '%' 开头，'%'成为组件实体内部的 引用功能 符号。
4.同时，url中非保留字只有ascii英文字母和4个标点字符，所以需要将汉字转码成某种编码明文才能使用，至于那种编码是由网站自身决定的，例子中便是Unicode编码明文。
"""
"""url中的保留字符(url组件分割功能符号)
  reserved    = gen-delims / sub-delims

  gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"

  sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
              / "*" / "+" / "," / ";" / "="

  inside-quote = "%"（编码明文引用符号）
"""

"""
url组件实体内部字符组成：
1>> ALPHA / DIGIT / "-" / "." / "_" / "~"				--> 不变
2>> 汉字以及汉字标点符号								--> 转码成编码明文成为 '\u4e2d' / '\xad'格式（编码明文引用）
3>> 除非保留字符以外的ASCII其他可打印字符 				--> 直接进行url引用（编码明文引用）
4>> ASCII中的利用'\'转义的特殊字符						--> 通过repr()表述后会转换为r'\n'明文（编码明文引用）
##tip:unicode不止u'\u4e2d'，还有形如u'\xb7'('·'垂直居中点号)
"""



import urllib
import re

# 注意，urllib.quote 虽然只针对组件内部用户参数名或参数值中间的以保留字符作为内容的引用
# 对于汉字只会将相应编码翻译成编码明文，不存在汉字编码转码操作 #tip:urllib.quote(...)本身是不具有编码转码功能的，其只有一个形参
# 如：同样的urllib.quote('中')，在不同的编码环境中进行引用结果是不一样的，汉字本身是什么编码就转化成相应编码明文
#   环境		中文编码格式	编码			明文
#   idle		gb2312			\xd6\xd0		%D6%D0
#   文档首行	utf-8			\xe4\xb8\xad	%E4%B8%AD
# 所以在进行url引用前，应先将组件值转化到目标编码，只是对Unicode编码不能直接利用quote引用

# 对目标组件实体为非Unicode编码明文，直接(若需要编码转码要先转码)利用urllib.quote(...)进行url编码

# 对目标组件实体为Unicode编码明文，需要特殊处理
# 策略：对汉字和ASCII两类分别进行操作
# 1.汉字转码成明文编码操作
# 2.ASCII字符直接进行urllib.quote(...)操作，非保留字符不变，其余字符引用
# 具体实现：
# 1.将原来编码转化成Unicode，这样保持一个字符一个字，便于处理
# 2.将目标字符串打散成由 {索引:相应字符} 的字典，索引(位置)很关键
# 3.将字符串字典中的ASCII字符与非ASCII字符分开成两个字典
# 4.对ASCII字符逐个进行url引用
# 5.将汉字转换成目标编码(若需要)，并利用repr表述成编码明文，对'\u'进行 '%u'替换，并且全部去大写
#   同时，汉字的编码一般为大写'%E4%B8%AD'，而非'%e4%b8%ad'；所以需要对汉字与ASCII进行区别处理
# 6.利用索引进行拼接，返回url编码字符串明文
def quote_uni(foo, code_ori, code_aim='unicode'):
	code_ori = code_ori.lower()
	code_aim = code_aim.lower()

	if code_aim == 'unicode':
		if code_ori != 'unicode':
			foo = foo.decode(code_ori)

		dict_foo = dict(zip(range(len(foo)), foo))

		asciis = {}
		for i in range(128):
		    asciis[i] = chr(i)
		asciis_values = asciis.values()

		ascii_foo = {}
		for i in dict_foo:
			x = dict_foo[i]
			if x in asciis_values:
				ascii_foo[i] = x

		chn_foo = {}
		chn_foo_keys = list(set(dict_foo) - set(ascii_foo))
		for i in chn_foo_keys:
			chn_foo[i] = dict_foo[i]

		for i in ascii_foo:
			ascii_foo[i] = urllib.quote(str(ascii_foo[i]))

		for i in chn_foo:
			a = repr(chn_foo[i])
			a = a[2:len(a)-1]
			if a.startswith('\\u'):
				chn_foo[i] = a.upper().replace('\\U', '%u')
			else:
				chn_foo[i] = a.upper().replace('\\X', '%')

		ascii_foo.update(chn_foo)

		quote_foo = ''
		for i in sorted(ascii_foo.keys()):
			quote_foo += ascii_foo[i]

	elif code_ori == 'unicode':
		quote_foo = urllib.quote(foo.encode(code_aim))
	elif code_ori != 'unicode' and code_ori == code_aim:
		quote_foo = urllib.quote(foo)
	elif code_ori != 'unicode' and code_ori != code_aim:
		quote_foo = urllib.quote(foo.decode(code_ori).encode(code_aim))

		# print 'dict_foo:', dict_foo, '\nascii_foo:', ascii_foo, '\nchn_foo:', chn_foo, '\nquote_foo:', quote_foo
	return quote_foo


# 对url组件实体为非Unicode的明文：
# 同为'foo_5中文#/\n'，在经过utf-8 / gb2312 url编码后分别在解码
#   urllib.unquote('foo_5%E4%B8%AD%E6%96%87%23/%0A')  -->  'foo_5\xe4\xb8\xad\xe6\x96\x87#/\n'
#   urllib.unquote('foo_5%D6%D0%CE%C4%23/%0A')  -->  'foo_5\xd6\xd0\xce\xc4#/\n'
# 所以对url组件实体为非Unicode的明文，可以直接利用urllib.unquote(...)将实体解码，得到成明文编码对应编码字符串,
# 再利用中间码unicode转换到需要的目标编码

# 对url组件实体为Unicode的明文：
#   my_url_quote('foo_5中文#/\n%u', 'utf-8', 'unicode')  -->  'foo_5%u4E2D%u6587%23/%0A%25u'
#   urllib.unquote('foo_5%u4E2D%u6587%23/%0A%25u')  -->  'foo_5%u4E2D%u6587#/\n%u'
#   在Unicode明文中'%'完全是引用功能符号，可在明文中正则提取出汉字
# 策略：
# 1.利用正则表达式，将原始url编码Unicode明文以汉字切割出ASCII片段，解码
# 2.利用正则表达式，将原始url编码Unicode明文中的汉字提取出来，处理
# 3.将两者拼接

# 注意，在编码明文前后各加上识别标识符'z',保证明文开始结束都为ASCII字符，最后再去掉首尾'z'标识符
# ASCII字符串片段与中文片段相间有序排列，可利用此规律来拼接
# 片段['zfoo_5', '', '%23/%0A%25uz'] ['%u4E2D', '%u6587']
# 片段索引[0,2,4][1,3]
# 为了避免明文码与目标码一致重复中文转码-再转码无用操作，宁可做判断
def unquote_uni(foo, code_literal='unicode', code_aim='utf-8'):
	code_literal = code_literal.lower()
	code_aim = code_aim.lower()

	if code_literal == 'unicode':
		foo = 'z' + foo + 'z'
		pattern = r'%u[0-9A-Za-z]{4}'
		asciis = re.split(pattern, foo)
		chns = re.findall(pattern, foo)
		# print asciis, chns

		ascii_foo = dict(zip((x*2 for x in range(len(asciis))), asciis))
		chn_foo = dict(zip((x*2+1 for x in range(len(chns))), chns))
		# print ascii_foo, chn_foo

		for i in ascii_foo.keys():
			x = ascii_foo[i]
			if len(x) in (0,1):
				ascii_foo[i] = urllib.unquote(x)
			else:
				chn_foo.update({i:x})
				ascii_foo.pop(i)
		# print ascii_foo, chn_foo

		for i in chn_foo:
			x = chn_foo[i]
			if len(x) == 6:
				chn_foo[i] = eval('u\'' + x.replace('%u', '\\u') + '\'')
			else:
				chn_foo[i] = eval('u\'' + x.replace('%', '\\x') + '\'')

		# print ascii_foo, chn_foo

		ascii_foo.update(chn_foo)

		unquote_foo = ''
		for i in sorted(ascii_foo.keys()):
			unquote_foo += ascii_foo[i]

		unquote_foo = unquote_foo[1:len(unquote_foo)-1]

		if code_aim != 'unicode':
			unquote_foo = unquote_foo.encode(code_aim)
	elif code_literal == code_aim:
		unquote_foo = urllib.unquote(foo)
	elif code_literal != code_aim and code_aim == 'unicode':
		unquote_foo = urllib.unquote(foo).decode(code_literal)
	elif code_literal != code_aim and code_aim != 'unicode':
		unquote_foo = urllib.unquote(foo).decode(code_literal).encode(code_aim)

	return unquote_foo
"""
def builname_urlencode():
	buildings = {'11089486':'17幢(规划二期C组团3#楼)'}

	for bu in buildings:
		print 'update t_P_building_appoint set builname = \'%s\' where state != 1 and builkeyid = \'%s\';' % (my_url_quote(buildings[bu], 'utf-8', 'unicode'), bu)
		# print '\'%s\':\'%s\',' % (bu, my_url_quote(buildings[bu], 'utf-8', 'unicode')) 
		# print bu, my_url_quote(buildings[bu], 'utf-8', 'unicode')

def builname_urldecode():
	buildings = {'11106636':'S1%u53F7%u697C'}

	for bu in buildings:
		print bu, my_url_unquote(buildings[bu], 'unicode', 'utf-8')"""


if __name__ == '__main__':
	# builname_urlencode()
	# builname_urldecode()

	# print my_url_unquote('19%u5E62%uFF08%u89C4%u5212%u4E3A3%u53F7%u697C%uFF09', 'unicode', 'utf-8')
	print quote_uni('34幢', 'utf-8', 'unicode')
	print unquote_uni('34%b4%b1', 'gb2312', 'utf-8')
else:
	pass
